﻿using Fusion.Utilities.Generics;
using Fusion.Utilities.Singleton;
using System.Collections;
using UnityEngine;

namespace Fusion.Frameworks.Assets
{
    public class AssetData
    {
        private AssetBundle assetBundle = null;
        private int tempCount = 0;
        private int persistentCount = 0;
        private Coroutine releaseCoroutine = null;

        private bool releaseMark = false;

        public int ReferenceCount
        {
            get { return tempCount + persistentCount; }
        }

        public AssetBundle AssetBundle { get => assetBundle; set => assetBundle = value; }
        public Coroutine ReleaseCoroutine { get => releaseCoroutine; set => releaseCoroutine = value; }
        public bool ReleaseMark { get => releaseMark; set => releaseMark = value; }
        public int TempCount { get => tempCount; }
        public int PersistentCount { get => persistentCount;}

        public AssetData(AssetBundle assetBundle)
        {
            this.assetBundle = assetBundle;
        }

        public AssetData()
        {
        }

        public void Reference(bool persistent = false, int count = 1)
        {
            if (persistent)
            {
                persistentCount += count;
            }
            else
            {
                if (releaseMark == true)
                {
                    releaseMark = false;
                    tempCount = 0;
                }
                tempCount += count;
            }

        }
        public void Release(int count = 1, bool persistent = false)
        {
            if (persistent)
            {
                if (persistentCount > 0)
                {
                    persistentCount -= count;
                    if (persistentCount < 0)
                    {
                        persistentCount = 0;
                    }
                }
            }
            else
            {
                if (tempCount > 0)
                {
                    tempCount -= count;
                    if (tempCount < 0)
                    {
                        tempCount = 0;
                    }
                }
            }
        }

        public void ReleaseAssetBundle()
        {
            assetBundle.Unload(true);
        }

    }

    /// <summary>
    /// 记录AssetBundle的引用数量，当引用数量为0时会销毁AssetBundle以及加载出来的资源
    /// </summary>
    [DisallowMultipleComponent]
    public class AssetReferences : SingletonBehaviour<AssetReferences>
    {
        private OrderDictionary<string, AssetData> assetReferences = new OrderDictionary<string, AssetData>();
        private float releaseDelay = 4.0f;

        public void Reference(AssetBundle assetBundle, bool persistentAsset = false, int count = 1)
        {
            string name = assetBundle.name;
            AssetData assetData = Reference(name, persistentAsset, count);
            assetData.AssetBundle = assetBundle;
        }

        public AssetData Reference(string assetBundleName, bool persistentAsset = false, int count = 1)
        {
            string name = assetBundleName;
            AssetData assetData;
            bool result = assetReferences.TryGetValue(name, out assetData);
            if (result)
            {
                if (assetData.ReleaseCoroutine != null)
                {
                    StopCoroutine(assetData.ReleaseCoroutine);
                    assetData.ReleaseCoroutine = null;
                }
                assetData.Reference(persistentAsset);
            }
            else
            {
                assetReferences[name] = new AssetData();
                assetReferences[name].Reference(persistentAsset);
                assetData = assetReferences[name];
            }
            return assetData;
        }

        public void Release(AssetBundle assetBundle, int count = 1, bool persistentAsset = false)
        {
            string name = assetBundle.name;
            Release(name, count, persistentAsset);
        }

        public void ReleaseImmediate(AssetBundle assetBundle, int count = 1, bool persistentAsset = false)
        {
            string name = assetBundle.name;
            ReleaseImmediate(name, count, persistentAsset);
        }

        private void ReleaseAssetData(string name)
        {
            AssetData assetData;
            if (assetReferences.TryGetValue(name, out assetData))
            {
                assetData.ReleaseAssetBundle();
                assetReferences.Remove(name);
            }
        }
        private IEnumerator ReleaseAssetDataCountdown(string name)
        {
            yield return new WaitForSecondsRealtime(releaseDelay);
            ReleaseAssetData(name);
        }

        public void Release(string name, int count = 1, bool persistentAsset = false)
        {
            name = name.ToLower();
            AssetData assetData;
            if (assetReferences.TryGetValue(name, out assetData))
            {
                assetData.Release(count, persistentAsset);
                if (assetReferences[name].ReferenceCount == 0)
                {
                    assetData.ReleaseCoroutine = StartCoroutine(ReleaseAssetDataCountdown(name));
                }
            }
        }

        public void ReleaseImmediate(string name, int count = 1, bool persistentAsset = false)
        {
            name = name.ToLower();
            AssetData assetData;
            if (assetReferences.TryGetValue(name, out assetData))
            { 
                assetData.Release(count, persistentAsset);
                if (assetReferences[name].ReferenceCount == 0)
                {
                    ReleaseAssetData(name);
                }
            }
        }

        public AssetBundle Get(string name)
        {
            name = name.ToLower();
            AssetData assetData;
            bool result = assetReferences.TryGetValue(name, out assetData);
            if (result)
            {
                return assetData.AssetBundle;
            }
            else
            {
                return null;
            }
        }

        public void SetReleaseMark(bool value)
        {
            int count = assetReferences.Count;
            for (int index = 0; index < count; index++) 
            {
                assetReferences.Get(index).ReleaseMark = value;
            }
        }

        public void Clear()
        {
            StopAllCoroutines();
            int count = assetReferences.Count;
            for (int index = count - 1; index >= 0; index--)
            {
                AssetData data = assetReferences.Get(index);
                if (data.ReleaseMark)
                {
                    ReleaseImmediate(data.AssetBundle, data.TempCount);
                }
            }
        }
    }
}

